#pragma once 

#include <iostream>
#include <string>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "Log.hpp"

using namespace std;

using func_t = std::function<std::string(const std::string&)>;   //using xxx = 类型，是C++风格的typede
//typedef std::function<std::string(const std::string&)> func_t;

Log lg;

enum{
    SOCKET_ERR=1,
    BIND_ERR
};

uint16_t defaultport = 8080;    //端口号是一个16位无符号整数
uint32_t defaultip = INADDR_ANY;    //ip地址是一个32位无符号整数 #define	INADDR_ANY		((in_addr_t) 0x00000000)
const int sz = 1024;

class UdpServer
{
public:
    UdpServer(uint16_t port = defaultport, string ip = to_string(defaultip)) //运行的时候，只需要知道端口号和ip即可
        : _sockfd(0)
        , _ip(ip)
        , _port(port)
        , _isrunning(false)
        {
            bzero(&_local, sizeof(_local));
            _local.sin_family = AF_INET;     //网络层协议族为IPv4
            _local.sin_addr.s_addr = inet_addr(_ip.c_str());      //将ip字符串转换为网络字节序的32位整数
            _local.sin_port = htons(_port);     //将端口号转换为网络字节序的16位整数

            /*           
            local.sin_family = AF_INET;
            local.sin_port = htons(port_); //需要保证我的端口号是网络字节序列，因为该端口号是要给对方发送的
            local.sin_addr.s_addr = inet_addr(ip_.c_str()); //1. string -> uint32_t 2. uint32_t必须是网络序列的 // ??
            // local.sin_addr.s_addr = htonl(INADDR_ANY);
            /*
            单网络接口：如果你的服务器只有一个网络接口，设置 INADDR_ANY 意味着无论客户端通过哪个IP地址连接到这个服务器，服务端都会接受连接。
            多网络接口：如果你的服务器有多个网络接口，每个接口有不同的IP地址，设置 INADDR_ANY 则意味着无论客户端连接到哪个IP地址，服务端都会接受连接。
            例如，服务器可能有一个IP地址用于内部网络，另一个IP地址用于外部网络。
            多IP地址：如果服务器的一个网络接口配置了多个IP地址（比如通过虚拟接口或别名），设置 INADDR_ANY 允许服务端在这些所有IP地址上接受连接。

            也就是说，如果这个服务器存在多个ip，那么客户端与服务器通信时，可以接入本服务器的任意一个ip
            */


            //将peer暂时初始化，后续会获得peer的地址信息
            bzero(&_peer, sizeof(_peer));
            _peer.sin_family = AF_INET;
            _peer.sin_addr.s_addr = INADDR_ANY;
            _peer.sin_port = htons(0);
        }

    ~UdpServer()
    {
        if (_sockfd) close(_sockfd);
    }

public:
    void Init()
    {
        //1.创建UDP套接字
        _sockfd = socket(AF_INET,SOCK_DGRAM, 0);      //int socket(int domain, int type, int protocol);
        if (_sockfd < 0)
        {
            lg(Fatal, "socket create error, sockfd: %d", _sockfd);
            exit(SOCKET_ERR);
        }

        lg(Info, "socket create success, sockfd: %d", _sockfd);


        //绑定端口号和ip地址
        if (bind(_sockfd, (const struct sockaddr*)&_local, sizeof(_local)) < 0)
        {
            lg(Fatal, "bind error, sockfd: %d", _sockfd);
            exit(BIND_ERR);
        }

        lg(Info, "bind success, sockfd: %d, ip: %s, port: %d", _sockfd, _ip.c_str(), _port);
    }

    //对代码进行分层
    void Run(func_t func)       //这个地方需要传入一个函数指针
    {
        _isrunning = true;
        char buffer[sz] = {0};
        while (_isrunning)
        {
            socklen_t len = sizeof(_peer);

            cout << "recv not over" << endl;

            ssize_t n = recvfrom(_sockfd, buffer, sz, 0, (struct sockaddr*)&_peer, &len);
            if (n < 0)
            {
                lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));
                continue;   //出错了，继续接收下一个数据包
            }

            cout << "recv over " <<endl;
            
            buffer[n] = 0;
            string data = buffer;
            string echo = func(data);
            sendto(_sockfd, echo.c_str(), echo.size(), 0, (const struct sockaddr*)&_peer, len);
        }
    }

private:
    int _sockfd;        //套接字描述符,一切皆文件
    string _ip;     //我们使用的ip一般是字符串样式
    uint16_t _port;
    bool _isrunning;
    struct sockaddr_in _local;    
    struct sockaddr_in _peer;
};
